//=========================================================================
// Copyright (C) 2024 The C++ Component Model(COMO) Open Source Project
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//      http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
//=========================================================================

#include <stdio.h>
#include <time.h>
#include <unistd.h>
#include <stdlib.h>
#include <syslog.h>
#include <stdbool.h>
#include <sys/select.h>
#include "voter_lib.h"        // rudp.h included
#include "voter_client.h"
#include "voter_message.h"

extern Rudp rudp;

#define MAX_BUF_SIZE 25600

// Check command line arguments
// mode = 1: TCP, 2: UDP, 3: TCP with debug, 4: UDP with debug
int checkArguments(int argc, char **argv)
{
    int mode = 1; //默认为TCP, 无debug

    if (argc < 4) {
        printf("Usage: %s <-t/-u> <host> <port>\n", argv[0]);
        printf("\t-t\tUse TCP\n");
        printf("\t-u\tUse UDP\n");
        exit(1);
    }
    if ((argc == 5) && (argv[4][1] == 'd')) {
        mode += 2;
    }
    if (strcmp(argv[1], "-u") == 0) {
        mode += 1;
    }
    printf("mode = %d\n", mode);
    return mode;
}

void sleep_until(int year, int month, int day, int hour, int minute, int second)
{
    // 构建 tm 结构，表示目标时间
    struct tm target_tm = {};
    target_tm.tm_year = year - 1900;  // tm_year 是从 1900 年开始
    target_tm.tm_mon = month - 1;     // tm_mon 是从 0 开始
    target_tm.tm_mday = day;
    target_tm.tm_hour = hour;
    target_tm.tm_min = minute;
    target_tm.tm_sec = second;

    // 将 tm 结构转换为 time_t（自 1970 年以来的秒数）
    time_t target_time_t = mktime(&target_tm);
    if (-1 == target_time_t) {
        perror("Error converting time");
        return;
    }

    // 将 time_t 转换为 timespec 结构
    struct timespec target_time;
    target_time.tv_sec = target_time_t;
    target_time.tv_nsec = 0;

    // struct timespec current_time;
    // clock_gettime(CLOCK_REALTIME, &current_time);

    // // 计算与目标时间的差值
    // long sec_diff = target_time.tv_sec - current_time.tv_sec;
    // long nsec_diff = target_time.tv_nsec - current_time.tv_nsec;

    // if (nsec_diff < 0) {
    //     nsec_diff += 1000000000;  // 修正秒差，确保纳秒差是正数
    //     sec_diff -= 1;
    // }

    // // 如果目标时间在当前时间之前，直接返回
    // if (sec_diff < 0 || (sec_diff == 0 && nsec_diff <= 0)) {
    //     printf("Target time is in the past!\n");
    //     return;
    // }

    // struct timespec ts = { sec_diff, nsec_diff };

    // 使用 clock_nanosleep 来等待直到目标时间
    int ret = clock_nanosleep(CLOCK_REALTIME, TIMER_ABSTIME, &target_time, NULL);
    if (ret == -1) {
        perror("clock_nanosleep failed");
    }
}

void cyclic_send(int s, int isTCP)
{
    // Pre-allocate memory for variables in while()
    int id_trans = 0;
    struct timespec now;
    struct timespec target_time;
    struct tm *target_tm;
    int interval_s = 2;

    while(true){
        // Currently set an 1s cycle
        clock_gettime(CLOCK_REALTIME, &now);
        target_time = now;
        target_time.tv_sec += interval_s;
        target_tm = localtime(&target_time.tv_sec);

        printf("(%d)Sleeping for %d seconds...\n", id_trans, interval_s);
        sleep_until(target_tm->tm_year + 1900, target_tm->tm_mon + 1, target_tm->tm_mday,
                    target_tm->tm_hour, target_tm->tm_min, target_tm->tm_sec);

        printf("(%d)Woke up, sendVoterMessage...\n", id_trans);
        sendVoterMessage(s, VOTER_CMD_VOTING_DATA, id_trans, 0, id_trans, NULL, 0, isTCP);
        //sendVoterMessage(s, VOTER_CMD_VOTING_BIGDATA, id_trans, 0, 0, NULL, 0, isTCP);
        id_trans++;
    }
}

void cyclic_receive(int s)
{
    fd_set readfds;
    int retval;
    char readbuf[MAX_BUF_SIZE];

    while (true){
        FD_ZERO(&readfds);
        FD_SET(s, &readfds);
        VoterMessage *message = (VoterMessage *)readbuf;
        int str_len = 0;

        retval = select(s+1, &readfds, NULL, NULL, NULL);
        if (retval == -1){
            perror("select()");
        }
        else if (retval > 0){
            // Read <sizeof(VoterMessage)> Bytes
            // Read until reaching the end, to avoid reading incomplete message
            int nread = ReadNBytes(s, readbuf, sizeof(VoterMessage) - sizeof(message->buf));
            str_len = message->len - (sizeof(VoterMessage) - sizeof(message->buf));

            // Error or short read means that the socket was closed
            if (nread < (int)(sizeof(VoterMessage) - sizeof(message->buf))) {
                syslog(LOG_ERR, "Disconnected client fd=%d", s);
            }

            // If more in buf, we read more
            if (str_len > 0) {
                 
                // nread_add == length of additionally read
                int nread_add = ReadNBytes(s, readbuf + sizeof(VoterMessage) - sizeof(message->buf), str_len);

                // Error or short read means that the socket was closed
                if (nread_add < str_len) {
                    syslog(LOG_ERR, "Disconnected client.\n");
                    continue;
                }
                nread += nread_add;
            }

            // Display received VoterMessage info
            if (-1 != nread){
                printf("Read %d bytes:\n", nread);
                // print_hex(readbuf, nread);
                // printVoterMessage(message);
                printVoterMsg("cyclic_receive", message);
            }
        }
        else {
            /**
             * No FD is ready in this turn.
             * In the future, this section can be used for periodic wake-ups
             * (even if there is no client activity)
             */
        }
    }
}

int main(int argc, char **argv)
{
    // Check & parse arguments
    int mode = checkArguments(argc, argv);
    bool isTCP = (mode % 2);
    int s;
    // Generic buffer
    char buf[MAX_VoterMessage_SIZE];

    // Create TCP Connection
    if (isTCP){
        printf("Using TCP\n");
        // argv[2] == server address，argv[3] == port
        s = createTCPConnection(argv[2], atoi(argv[3]));

        // Read signature
        ssize_t nread = read(s, buf, sizeof(buf));
        if (nread <= 0) {
            printf("Connection lost.\n");
            exit(1);
        }
            printf("Received signature from server.\n");
            //print_hex(buf, nread);
    #ifdef ZMQ_MODE
            ResponseZMQ(s, true);
            printf("Responsed to server: ZMQ_MODE\n");
    #else
            ResponseZMQ(s, false);
            printf("Responsed to server: NON_ZMQ_MODE\n");
    #endif
    }
    // Create UDP Connection
    else {
        printf("Using UDP\n");
        Rudp *U = RudpNew(&rudp, 1, 5);
        if (NULL == U) {
            ;
        }
        s = CreateUDPClient(atoi(argv[3]));
    }

    // 设定所有client在同一时间启动
    int year, month, day, hour, minute, second;
    if (argc == 10 || argc == 11){
        year = atoi(argv[4]);
        month = atoi(argv[5]);
        day = atoi(argv[6]);
        hour = atoi(argv[7]);
        minute = atoi(argv[8]);
        second = atoi(argv[9]);
    }

    // Enter the target time manually (with -h)
    // SHELL $ client_sender 127.0.0.1 7711 -h
    if (argc == 5 && argv[4][1] == 'h'){
        printf("Enter the target date and time (YYYY MM DD HH MM SS): ");
        if (scanf("%d %d %d %d %d %d", &year, &month, &day, &hour, &minute, &second) != 6) {
            fprintf(stderr, "Invalid input format.\n");
            return EXIT_FAILURE;
        }
    }

    // Sleep until the specified time
    printf("Sleeping until %d-%d-%d %d:%d:%d...\n", 
                                        year, month, day, hour, minute, second);
    sleep_until(year, month, day, hour, minute, second);

    // Sender (Send data for vote)
    if (argc == 10 || (argc == 11 && argv[10][1] == 's')){
        cyclic_send(s, isTCP);
    }
    // Receiver (Receive data from server)
    if (argc == 11 && argv[10][1] == 'r'){
        cyclic_receive(s);
    }

    return 0;
}
